#!/usr/bin/env python
# -*- coding: utf-8 -*-
import at
import os
import wmi
import time
import logging
import inspect
from ConfigParser import ConfigParser

class Daemon():    
    def __init__(self):
        self.logger = None
        self.__file = inspect.getfile(inspect.currentframe())
        self.__workRoot = os.path.abspath(os.path.dirname(self.__file))
        self.__configFile = os.path.join(self.__workRoot,"daemon.ini")
        self.__logfile = os.path.join(self.__workRoot,"daemon.log") 
        self.__monitors = []
        self.initConfig(self.__configFile)
        self.__wmic = wmi.WMI()
        
    def now(self,ftime=None):
        if ftime==None:
            ftime = time.time()
        return time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(ftime))
    
    def getRealFilePath(self,filePath):
        return os.path.abspath(filePath.replace("\\",os.sep).replace("/",os.sep))
    
    def checkRootExists(self,path):
        if os.path.isdir(path):
            if not os.path.exists(path):
                os.makedirs(path)
        else:
            fileRoot = os.path.dirname(path)
            if not os.path.exists(fileRoot):
                os.makedirs(fileRoot)
        return path

    def initLog(self,logfile):
        logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s %(levelname)s %(message)s',
                    datefmt="%Y-%m-%d %H:%M:%S",
                    filename= self.checkRootExists(os.path.abspath(logfile)),
                    filemode='a')
        console = logging.StreamHandler()
        console.setLevel(logging.INFO)
        formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
        console.setFormatter(formatter)
        logging.getLogger('').addHandler(console)
        return logging
    
    
    def initConfig(self,filename):
        filename = self.getRealFilePath(filename)
        config = ConfigParser()
        config.read(filename)
        if config.has_option("Daemon","logfile"):
            self.__logfile = config.get("Daemon","logfile")
            
        self.__logfile = self.getRealFilePath(self.__logfile)
        self.logger = self.initLog(self.__logfile)
        self.logger.info("Logger Server started")

        for section in config.sections(): 
            if config.has_option(section,"monitor") and config.get(section,"monitor")=="true":
                monitor = {}
                serviceName = section
                programPath = self.getRealFilePath(config.get(section,"startup"))
                if config.has_option(section,"programPath"):
                    programPath = config.get(section,"programPath")
                programName = os.path.basename(programPath).split(" ")[0]
                
                monitor["serviceName"] = serviceName
                monitor["programPath"] = programPath
                monitor["programName"] = programName
                monitor["onlyOne"] = True
                monitor["handleMax"] = 0
                monitor["memoryMax"] = 0
                    
                if config.has_option(section,"onlyOne"):
                    monitor["onlyOne"] = config.get(section,"onlyOne")=="true"
                
                if config.has_option(section,"handleMax"):
                    monitor["handleMax"] = int(config.get(section,"handleMax"))
            
                if config.has_option(section,"memoryMax"):
                    monitor["memoryMax"] = float(config.get(section,"memoryMax"))
                
                self.__monitors.append(monitor)
                pinfos=[]
                pinfos.append("serviceName:%s"%serviceName)
                pinfos.append("programName:%s"%programName)
                pinfos.append("onlyOne:%s"%monitor["onlyOne"])
                if monitor["handleMax"]>0:
                    pinfos.append("handleMax:%d"%monitor["handleMax"])
                else:
                    pinfos.append("handleMax:unlimited")
                    
                if monitor["handleMax"]>0:
                    pinfos.append("memoryMax:%sMB"%monitor["memoryMax"])
                else:
                    pinfos.append("memoryMax:unlimited")    
                msg = "Loaded monitor[%s]"%(",".join(pinfos))    
                self.logger.info(msg)
        
    def checkProgramService(self,serviceName,programName,onlyOne=True,handleMax=None,memoryMax=None):
        needRestart = False
        process = self.__wmic.Win32_Process(Name=programName)
        processCount = len(process)
        # check process is running or not
        if processCount==1:        
            #    Name:进程名
            #    ProcessId:进程PID
            #    ParentProcessId:父进程PID
            #    HandleCount:当前句柄数
            #    WorkingSetSize:当前占用内存字节
            proc = process[0]
            handle = int(proc.HandleCount)
            memory = float(proc.WorkingSetSize)/(1024*1024)
            if (handleMax and handle>=handleMax) or (memoryMax and memory>=memoryMax):
                procInfo=[]
                procInfo.append("Name:%s"%proc.Name)
                procInfo.append("ProcessId:%s"%proc.ProcessId)
                procInfo.append("HandleCount:%d"%handle)
                procInfo.append("Memory:%.2fMB"%memory)
                res = proc.Terminate()            
                if handleMax:
                    procInfo.append("handleMax:%d"%handleMax)
                if memoryMax:
                    procInfo.append("memoryMax:%.2fMB"%memoryMax)
                ok = "Failed"
                if len(res)>0 and res[0]==0:
                    ok = "Success"
                    needRestart = True
                msg = "Killed Process [%s] %s,handle or memory over limit value"%(",".join(procInfo),ok)
                self.logger.error(msg)
        elif processCount==0:
            msg = "Not found %s process"%programName
            services = self.__wmic.Win32_Service(Name=serviceName,State="Stopped")
            servicesCount = len(services)
            if servicesCount==1:
                service = services[0]
                res = service.StartService()
                ok = "Failed"
                if len(res)>0 and res[0]==0:
                    ok = "Success"
                msg = "%s,Start %s %s"%(msg,service.Name,ok)
                self.logger.info(msg)
            else:
                msg = "%s,Server %s is not install"%(msg,serviceName)
                self.logger.error(msg)
        else:
            if onlyOne:
                self.logger.error("Found %d process,will be kill all process"%processCount)
                for proc in process:
                    procInfo=[] 
                    handle = int(proc.HandleCount)
                    memory = float(proc.WorkingSetSize)/(1024*1024)
                    procInfo.append("Name:%s"%proc.Name)
                    procInfo.append("ProcessId:%s"%proc.ProcessId)
                    procInfo.append("HandleCount:%d"%handle)
                    procInfo.append("Memory:%.2f"%memory)
                    res = proc.Terminate()
                    ok = "Failed"
                    if len(res)>0 and res[0]==0:
                        ok = "Success"
                        needRestart = True
                    msg = "Killed Process [%s] %s"%(",".join(procInfo),ok)
                    self.logger.error(msg)
        if needRestart:
            return self.checkProgramService(serviceName,programName,onlyOne,handleMax,memoryMax)
        
    def main(self):
        #self.logger.info("Scan monitors ...")
        for monitor in self.__monitors:
            serviceName = monitor["serviceName"]
            programName = monitor["programName"]
            handleMax = monitor["handleMax"]
            memoryMax = monitor["memoryMax"]
            onlyOne = monitor["onlyOne"]
            #self.logger.info("Check %s %s ..."%(serviceName,programName))
            self.checkProgramService(serviceName,programName,onlyOne=onlyOne,handleMax=handleMax,memoryMax=memoryMax)
            
    def start(self):
        self.main()
        at.every().minutes.do(self.main)
        at.start()
            
    def stop(self):
        at.stop()

if __name__=='__main__':
    daemon = Daemon()
    daemon.start()